﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.DirectoryServices;
using System.Linq;
using System.Security.Principal;
using System.Web.UI;
using System.Web.UI.WebControls;
using ActiveDs;
using Library;

namespace DirectoryWeb
{
  /// <summary>
  /// グループリスト画面のフォームです。
  /// </summary>
  public partial class GroupList : Page
  {
    #region プライベートフィールド
    //プライマリ グループのメンバー格納用(グループのプライマリ グループ トークン, メンバーのパスとオブジェクトの種類のコレクション)
    private Dictionary<int, Dictionary<string, CategoryType>> primaryGroupMembers;
    #endregion

    #region イベントハンドラ
    protected void GroupsDataSource_Selected(object sender, ObjectDataSourceStatusEventArgs e)
    {
      this.CountLabel.Text = String.Format("{0} 個のオブジェクト", ((ICollection)e.ReturnValue).Count);
    }

    protected void DetailDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
    {
      if (this.GroupListBox.SelectedIndex == -1)
      {
        e.Cancel = true;
      }
    }

    protected void DetailDataSource_Selected(object sender, ObjectDataSourceStatusEventArgs e)
    {
      if (ViewState["primaryGroupMembers"] == null)
      {
        primaryGroupMembers = new Dictionary<int, Dictionary<string, CategoryType>>();
        ViewState["primaryGroupMembers"] = primaryGroupMembers;
        this.RelatingPanel.Visible = true;
      }
      else
      {
        primaryGroupMembers = (Dictionary<int, Dictionary<string, CategoryType>>)ViewState["primaryGroupMembers"];
      }

      var group = (DomainGroup)e.ReturnValue;
      this.ShowMembers(group);  //メンバーを表示
      this.ShowBelongGroups(group.Entry);   //所属するグループを表示

      this.InRoleButton.Enabled = this.GroupGridView.Rows.Count > 0;
      if (this.InRoleButton.Enabled)
      {
        this.InRoleButton.OnClientClick = String.Format(
          "showInRoleGroupList('{0}', '{1}')", this.GroupListBox.SelectedValue, CategoryType.Group);
      }
      this.InRoleUpdatePanel.Update();

      this.MemberButton.Enabled = this.MemberGridView.Rows.Count > 0;
      if (this.MemberButton.Enabled)
      {
        this.MemberButton.OnClientClick = String.Format("showAllMemberList('{0}')", this.GroupListBox.SelectedValue);
      }
      this.MemberUpdatePanel.Update();
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
      Response.Redirect("Main.aspx?idx=1");
    }
    #endregion

    #region プライベートメソッド
    /// <summary>
    /// メンバーを表示します。
    /// </summary>
    /// <param name="group">グループ。</param>
    private void ShowMembers(DomainGroup group)
    {
      var table = CreateDataSourceTable();  //データソース用のテーブルを作成
      CategoryType objectType;  //ディレクトリ オブジェクトの種類

      foreach (IADs member in group.Native.Members())
      {
        var row = table.NewRow();
        objectType = (CategoryType)Enum.Parse(typeof(CategoryType), member.Class, true);
        if (objectType == CategoryType.ForeignSecurityPrincipal)  //外部のセキュリティ プリンシパルの時
        {
          var objectSid = (byte[])member.Get("objectSid");
          var sid = new SecurityIdentifier(objectSid, 0);   //SID
          var account = (NTAccount)sid.Translate(typeof(NTAccount));  //アカウントに変換
          row[0] = account.Value;   //NT AUTHORITY\○○ => ToStringメソッドでも同じ
        }
        else  //外部のセキュリティプリンシパル以外の時
        {
          row[0] = member.Get("cn").ToString();
        }
        row[1] = DirectoryAccess.GetBelongPath(member);   //所属パスを取得
        table.Rows.Add(row);
      }
      this.AddPrimaryGroupMembers(group, table);  //プライマリ グループになっているメンバーを追加

      this.MemberGridView.DataSource = new DataView(table) { Sort = "Name" };
      this.MemberGridView.DataBind();
    }

    /// <summary>
    /// データソース用のテーブルを作成します。
    /// </summary>
    /// <returns>データソース用のテーブル。</returns>
    private DataTable CreateDataSourceTable()
    {
      var table = new DataTable();
      table.Columns.Add(new DataColumn("Name", typeof(string)));
      table.Columns.Add(new DataColumn("Path", typeof(string)));
      return table;
    }

    /// <summary>
    /// プライマリ グループになっているメンバーを追加します。
    /// </summary>
    /// <param name="group">グループ。</param>
    /// <param name="table">メンバーのバインド用のテーブル。</param>
    private void AddPrimaryGroupMembers(DomainGroup group, DataTable table)
    {
      var token = DirectoryAccess.GroupTokens.Single(item => item.Value.Equals(group.Name)).Key;  //グループのプライマリ グループ トークン
      Dictionary<string, CategoryType> primaryMembers;  //プライマリ グループのメンバー(パスとオブジェクトの種類のコレクション)

      if (primaryGroupMembers.TryGetValue(token, out primaryMembers))   //格納済の時
      {
        if (primaryMembers.Count == 0)
        {
          return;
        }
      }
      else  //未格納の時
      {
        //プライマリ グループ トークンのグループをプライマリ グループとしているメンバーのDirectoryEntryのコレクションを取得
        var primaryMemberEntries = DirectoryAccess.GetPrimaryGroupMemberEntries(token);
        primaryMembers = new Dictionary<string, CategoryType>(primaryMemberEntries.Count);
        primaryGroupMembers.Add(token, primaryMembers);
        if (primaryMemberEntries.Count == 0)
        {
          return;
        }

        foreach (var entry in primaryMemberEntries)   //取得メンバー数分
        {
          primaryMembers.Add(entry.Path, (CategoryType)Enum.Parse(typeof(CategoryType), entry.SchemaClassName, true));
          entry.Close();
        }
      }

      foreach (var member in primaryMembers)  //プライマリ グループになっているメンバー数分
      {
        var row = table.NewRow();
        row[0] = DirectoryAccess.PathToCn(member.Key);  //名前を取得
        row[1] = DirectoryAccess.GetBelongPath(member.Key);   //所属パスを取得
        table.Rows.Add(row);
      }
    }

    /// <summary>
    /// 所属するグループを表示します。
    /// </summary>
    /// <param name="group">グループ。</param>
    private void ShowBelongGroups(DirectoryEntry group)
    {
      var table = CreateDataSourceTable();  //データソース用のテーブルを作成
      foreach (string groupPath in group.Properties["memberOf"])
      {
        var row = table.NewRow();
        row[0] = DirectoryAccess.PathToCn(groupPath);   //名前を取得
        row[1] = DirectoryAccess.GetBelongPath(groupPath);  //所属パスを取得
        table.Rows.Add(row);
      }

      this.GroupGridView.DataSource = new DataView(table) { Sort = "Name" };
      this.GroupGridView.DataBind();
    }
    #endregion
  }
}